/*
 *  Copyright (C) 2004 Cidero, Inc.
 *
 *  Permission is hereby granted to any person obtaining a copy of 
 *  this software to use, copy, modify, merge, publish, and distribute
 *  the software for any non-commercial purpose, subject to the
 *  following conditions:
 *  
 *  The above copyright notice and this permission notice shall be included
 *  in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY IN CONNECTION WITH THE SOFTWARE.
 * 
 *  File: $RCSfile: CDSImageItem.java,v $
 *
 */

package com.cidero.upnp;

import java.awt.Image;
import java.util.logging.Logger;
import javax.swing.ImageIcon;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import org.cybergarage.xml.XML;

/**
 *
 *  Derived class of CDSItem used to represent generic video
 *  content. Serves as a base class for more specific classes, such as
 *  'movie'
 *  
 */
public class CDSImageItem extends CDSItem
{
  private static Logger logger = Logger.getLogger("com.cidero.upnp");

  static String upnpClass = "object.item.imageItem";

  // UPNP namespace fields
  String  longDescription;
  String  storageMedium;
  String  rating;

  // Dublin core namespace
  String  description;
  String  publisher;
  String  date;
  String  rights;

  public CDSImageItem()
  {
  }

  public CDSImageItem( Node node )
  {
    super( node );

    NodeList children = node.getChildNodes();

    for( int n = 0 ; n < children.getLength() ; n++ )
    {
      String nodeName = children.item(n).getNodeName();

      if( nodeName.equals("dc:description") )
        description = CDS.getSingleTextNodeValue( children.item(n) );
      else if( nodeName.equals("dc:date") )
        date = CDS.getSingleTextNodeValue( children.item(n) );
      else if( nodeName.equals("upnp:storageMedium") )
        storageMedium = CDS.getSingleTextNodeValue( children.item(n) );

    }

  }

  public void setLongDescription( String description ) {
    this.longDescription = description;
  }
  public String getLongDescription() { return longDescription; }

  public void setStorageMedium( String storageMedium ) {
    this.storageMedium = storageMedium;
  }
  public String getStorageMedium() { return storageMedium; }

  public void setRating( String rating ) {
    this.rating = rating;
  }
  public String getRating() { return rating; }


  public void setDescription( String description ) {
    this.description = description;
  }
  public String getDescription() { return description; }

  public void setPublisher( String publisher ) {
    this.publisher = publisher;
  }
  public String getPublisher() { return publisher; }

  public void setDate( String date ) {
    this.date = date;
  }
  public String getDate() { return date; }

  public void setRights( String rights ) {
    this.rights = rights;
  }
  public String getRights() { return rights; }


  /**
   *  Get object class
   *
   *  @return  UPNP class string
   */
  public String getUPNPClass() { return upnpClass; }

  public String attributesToXML( CDSFilter filter )
  {
    // No extra attributes for this class (just elements)
    return super.attributesToXML( filter );  
  }

  public String elementsToXML( CDSFilter filter )
  {
    StringBuffer buf = new StringBuffer();

    // Need to build element string starting with elements in base classes
    buf.append( super.elementsToXML( filter ) );

    if( (description != null) && filter.propertyEnabled( "dc:description" ) )
    {
      buf.append( "  <dc:description>" );
      buf.append( XML.escapeXMLChars(description) );
      buf.append( "</dc:description>\n" );
    }
    if( (publisher != null) && filter.propertyEnabled( "dc:publisher" ) )
    {
      buf.append( "  <dc:publisher>" );
      buf.append( XML.escapeXMLChars(publisher) );
      buf.append( "</dc:publisher>\n" );
    }
    if( (date != null) && filter.propertyEnabled( "dc:date" ) )
    {
      buf.append( "  <dc:date>" );
      buf.append( XML.escapeXMLChars(date) );
      buf.append( "</dc:date>\n" );
    }
    if( (rights != null) && filter.propertyEnabled( "dc:rights" ) )
    {
      buf.append( "  <dc:rights>" );
      buf.append( XML.escapeXMLChars(rights) );
      buf.append( "</dc:rights>\n" );
    }


    if( (longDescription != null) && filter.propertyEnabled( "upnp:longDescription" ) )
    {
      buf.append("  <upnp:longDescription>");
      buf.append( XML.escapeXMLChars(longDescription) );
      buf.append("</upnp:longDescription>\n");
    }
    if( (storageMedium != null) && filter.propertyEnabled( "upnp:storageMedium" ) )
    {
      buf.append("  <upnp:storageMedium>");
      buf.append( storageMedium );
      buf.append("</upnp:storageMedium>\n");
    }
    if( (rating != null) && filter.propertyEnabled( "upnp:rating" ) )
    {
      buf.append("  <upnp:rating>");
      buf.append( XML.escapeXMLChars(rating) );
      buf.append("</upnp:rating>\n");
    }

    return buf.toString();
  }
  
  /**
   *  Get image icon sized to fit in box defined by width and height.
   *  Use the smallest possible image resource at least as big as 
   *  the requested icon size.  
   *
   *  (TODO - add 'tolerance' here instead of strictly requiring resource 
   *  size to be >= request icon size ?)
   */
  public ImageIcon getImageIcon( int widthConstraint, int heightConstraint )
  {
    int bestMatchIndex = -1;
    int minWidthDiff = 99999;

    // Check for existing thumbnail of similar size (TODO: Add similar logic)
    ImageIcon imageIcon = getThumbImage();
    if( imageIcon != null )
      return imageIcon;

    logger.fine("CDSImageItem: getImageIcon nResources = " +
                       getResourceCount() );

    for( int n = 0 ; n < getResourceCount() ; n++ )
    {
      CDSResource res = getResource(n);
      int width = res.getWidth();
      int height = res.getHeight();
      
      // Make sure image protocol is supported by controller display code
      // Currently supported MIME types are:
      //  image/jpeg, image/bmp, image/png
      String protoInfo = res.getProtocolInfo();
      if( ( protoInfo.indexOf("image/jpeg") < 0 ) && 
          ( protoInfo.indexOf("image/bmp") < 0 ) &&
          ( protoInfo.indexOf("image/png") < 0 ) )
      {
        continue;
      }
      
      //logger.fine("Resource width, height = " +
      //                         width + " " + height );
      
      //      if( (width >= widthConstraint) && (height >= heightConstraint) )
      //      {
      if( Math.abs(width-widthConstraint) < minWidthDiff )
      {
        minWidthDiff = Math.abs(width-widthConstraint);
        bestMatchIndex = n;
      }

    }

    if( bestMatchIndex >= 0 )
    {
      imageIcon = getResource(bestMatchIndex).getImageIcon();
      
      int iconWidth = imageIcon.getIconWidth();
      int iconHeight = imageIcon.getIconHeight();
      logger.fine("Image Resource best match raw x,y size = " + 
                  iconWidth + " " + iconHeight );

      // Resize icon to exact requested size. First, figure out whether
      // width or height is limiting factor. 

      ImageIcon scaledIcon;

      float targetRatio = (float)widthConstraint/(float)heightConstraint;
      float actualImageRatio = (float)iconWidth/(float)iconHeight;

      float scaleFactor;
      int   scaledHeight;
      int   scaledWidth;
      
      logger.fine("targetRatio " + targetRatio + " Actual " +
                         actualImageRatio );
      if( targetRatio > actualImageRatio )
      {
        // height is limiting factor 
        scaleFactor = (float)heightConstraint/(float)iconHeight;
        scaledHeight = heightConstraint;
        scaledWidth = (int)((float)iconWidth*scaleFactor);
      }
      else
      {
        // width is limiting factor 
        scaleFactor = (float)widthConstraint/(float)iconWidth;
        scaledHeight =(int)((float)iconHeight*scaleFactor);
        scaledWidth = widthConstraint;
      }

      scaledIcon = new ImageIcon( imageIcon.getImage().
                                  getScaledInstance( scaledWidth, scaledHeight,
                                                     Image.SCALE_DEFAULT ) );
        
      // Associate the most recent thumbnail with the object (simple caching)
      setThumbImage( scaledIcon );
      
      return scaledIcon;
    }
    else
    {
      logger.warning("No best match found for image resource!");
      return null;
    }
  }

}
